package property

import (
	"fmt"
	"iot-base/common/cache"
	cpt "iot-base/common/cache/picktemplate"
	"iot-base/common/config"
	"iot-base/common/eventtype"
	"iot-base/common/logger"
	"iot-base/common/notify"
	"iot-base/common/params"
	"iot-base/common/propertytype"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/Knetic/govaluate"
)

func handleStruct(pValue interface{}, pContext *ValueContext, index int) {
	/*pItemValue := &propertytype.Value{
		TID:    pContext.ValuePtr.TID,
		PID:    pContext.ValuePtr.PID,
		Mindex: pContext.ValuePtr.Mindex,
		SubID:  pValue.SubID,
		Value:  pValue.Value,
	}*/
	svalue, ok := pValue.([]interface{})
	if ok {
		for i := range svalue {
			mapValue, okm := svalue[i].(map[string]interface{})
			if okm {
				subID, sok := mapValue["SubID"]
				if sok {
					sid, _ := subID.(string)
					value, _ := mapValue["Value"]
					pItemValue := &propertytype.Value{
						TID:    pContext.ValuePtr.TID,
						PID:    pContext.ValuePtr.PID,
						Mindex: index,
						SubID:  sid,
						Value:  value,
					}
					//go DataHandler(pItemValue)
					handle(pItemValue, pContext)
				}

			}
		}

	}

}
func handleRange(pValue interface{}, pContext *ValueContext) bool {
	prop := pContext.PropertyPtr
	if prop.HasRange {
		return pContext.FValue >= prop.Min && pContext.FValue <= prop.Max
	}
	if prop.HasEnumList && prop.EnumList != nil {
		//isExist := false
		for _, v := range *prop.EnumList {
			if v == pContext.IValue {
				//isExist := true
				return true
			}
		}
		return false

	}
	if prop.Type == "percentage" {
		return pContext.FValue >= 0 && pContext.FValue < 100.001
	}
	return true
}

//ReportData 上报数据
type ReportData struct {
	Data  interface{}
	Topic string
}

var reportChan chan ReportData

func SetDataChan(datachan chan ReportData) {
	reportChan = datachan
}
func makeValue(pContext *ValueContext) *params.ValueInRedis {

	pName, _ := pContext.Context["pname"].(string)
	subName, _ := pContext.Context["subname"].(string)
	changed, ok := pContext.Context["isChanged"]
	var isChanged bool = false
	if ok {
		isChanged, _ = changed.(bool)
	}
	var stationName string
	if isChanged {
		stationName = pContext.ThingPtr.GetStationName()
		//fmt.Println("data ----------changing----------")
	}
	return &params.ValueInRedis{
		Value:    pContext.ValuePtr.Value,
		Type:     pContext.PropertyPtr.Type,
		PickTime: pContext.PickTime.Unix(),
		FValue:   pContext.FValue,
		SValue:   pContext.SValue,
		IValue:   pContext.IValue,
		//IsSaved:    isSaved,
		PID:         pContext.ValuePtr.PID,
		SID:         pContext.ValuePtr.SubID,
		PIndex:      pContext.ValuePtr.Mindex,
		TID:         pContext.ValuePtr.TID,
		TName:       pContext.ThingPtr.Thing.SName,
		PName:       pName,
		SName:       subName,
		InsertTime:  time.Now().Unix(),
		Display:     pContext.Display,
		UnitName:    pContext.PropertyPtr.UnitName,
		IsChanged:   isChanged,
		StationName: stationName,
		ModelID:     pContext.ThingPtr.Thing.ModelID,
	}

}

func broadCastData(pContext *ValueContext) {
	pvinreds := makeValue(pContext)
	notify.DataReady(pvinreds)
}

func handleItem(pValue interface{}, pContext *ValueContext, index int) {
	pContext.Context["isSaveDB"] = false
	pContext.Context["isChanged"] = false
	pContext.Display = ""
	pContext.IValue = 0
	pContext.FValue = 0.0
	pContext.SValue = ""
	isEnable := cpt.IsPickEnable(pContext.ValuePtr.TID,
		pContext.ValuePtr.PID)
	if !isEnable {
		return
	}
	typeOK := handleType(pValue, pContext)
	if typeOK {
		rangeOK := handleRange(pValue, pContext)
		if rangeOK {
			saveData(pContext)
			isSave, ok := pContext.Context["isSaveDB"]
			if ok {
				saved, _ := isSave.(bool)
				/*
					if pContext.ValuePtr.TID == "k-W9xAW8liX8vrrRJaSLI" {
						fmt.Println("***********found*****to save***********", pContext.ValuePtr.Mindex, pContext.ValuePtr.PID,
							pContext.ValuePtr.SubID, isSave, pContext.Context["isChanged"], pContext.Display)
					}*/
				if saved && reportChan != nil {
					//fmt.Println("-------------data report----------")
					reportChan <- ReportData{
						Data:  pContext.ValuePtr,
						Topic: "data",
					}
				}
			}
			broadCastData(pContext)
			generateSelfEvent(pContext)
		} else {
			logger.SLog.Error("range error------------------", pValue, pContext.ValuePtr.TID,
				pContext.ValuePtr.PID, pContext.PropertyPtr)
		}
	} else {
		logger.SLog.Error("type error-------------type---not--ok--", pValue, pContext, pContext.PropertyPtr, typeOK)
	}

}
func changeNumber(pValue interface{}, pContext *ValueContext) bool {
	kind := reflect.TypeOf(pValue).Kind()
	if kind == reflect.Bool {
		svalue := "0"
		var v64 int64 = 0
		v64 = 0
		v, ok := pValue.(bool)
		if v && ok {
			svalue = "1"
			v64 = 1
		}
		pContext.ValuePtr.Value = v
		pContext.IValue = v64
		pContext.FValue = float64(v64)
		pContext.SValue = svalue
		return true
	}
	if kind >= reflect.Int && kind <= reflect.Uint64 {
		svalue := fmt.Sprintf("%d", pValue)
		v64, _ := strconv.ParseInt(svalue, 10, 64)
		pContext.ValuePtr.Value = v64
		pContext.IValue = v64
		pContext.FValue = float64(v64)
		pContext.SValue = svalue
		return true
		//isint = true
	} else if kind == reflect.Float32 || kind == reflect.Float64 {
		var large = true
		f32, ok := pValue.(float32)
		if ok {
			if f32 < 0 {
				f32 = -f32
			}
			if f32 <= 1 {
				large = false
			}
		} else {
			f64temp, ok2 := pValue.(float64)
			if !ok2 {
				return false
			}
			if f64temp < 0 {
				f64temp = -f64temp
			}
			if f64temp <= 1 {
				large = false
			}
		}
		var svalue = ""
		if large {
			svalue = fmt.Sprintf("%.2f", pValue)
		} else {
			svalue = fmt.Sprintf("%.3f", pValue)
		}

		f64, errf := strconv.ParseFloat(svalue, 64)
		if errf != nil {
			logger.SLog.Error("--------errf--------", errf)
			return false
		}
		pContext.ValuePtr.Value = f64
		pContext.IValue = int64(f64)
		pContext.FValue = f64
		pContext.SValue = svalue
		return true
	}
	return false
}

//此函数根据类型来翻译数据
func handleType(pValue interface{}, pContext *ValueContext) bool {
	prop := pContext.PropertyPtr
	//var svalue string
	//var strok bool = false
	svalue, strok := pValue.(string)
	pContext.Context["isStream"] = false
	//pContext.UnitName = prop.UnitName
	switch prop.Type {
	case "float64", "float", "float32", "percentage":
		pContext.Type = "float"
		if strok {

			if svalue == "" {
				pContext.IValue = 0
				pContext.FValue = 0.0
				pContext.Type = "float"
				pContext.ValuePtr.Value = "EMPTY"
				pContext.SValue = "EMPTY"
				pContext.Display = pContext.SValue
				return true
			}
			pContext.SValue = svalue
			v64, err := strconv.ParseFloat(svalue, 64)
			if err != nil {
				fmt.Println("type error -----parasefloat---1---v:", svalue, "--error:---------",
					err, prop.Type, pContext.ValuePtr.TID,
					prop.PID, prop.SubID)
				return false
			}
			pContext.IValue = int64(v64)
			pContext.FValue = v64
			pContext.Type = "float"
			pContext.ValuePtr.Value = v64
			pContext.Display = pContext.SValue

			return true
		} else {
			if !changeNumber(pValue, pContext) {
				fmt.Println("----------type error float----------", pValue, reflect.TypeOf(pContext.ValuePtr.Value).Name(),
					pContext.ValuePtr.TID,
					prop.PID, prop.SubID)
				return false
			}
			pContext.Display = pContext.SValue
			return true
		}

	case "text", "stream":
		pContext.Type = "text"
		pContext.Context["isStream"] = true
		pContext.SValue = svalue
		pContext.Display = pContext.SValue
		return strok
	case "int", "enum", "date":
		pContext.Type = "int"
		if strok {
			//
			fv, err := strconv.ParseFloat(svalue, 64)
			if err == nil {
				iv := int64(fv)
				pContext.ValuePtr.Value = iv
				svalue = strconv.FormatInt(iv, 10)
				pContext.SValue = svalue
				pContext.IValue = iv
				pContext.FValue = fv
				pContext.Display = svalue
				//return true
			} else {
				pContext.SValue = svalue
				pContext.Display = svalue
				fmt.Println("----------type error 1--int--------", err)
				return false
			}

		} else {
			if !changeNumber(pValue, pContext) {
				fmt.Println("----------type error 2----------", pValue, reflect.TypeOf(pContext.ValuePtr.Value).Name())
				return false
			}
			pContext.Display = pContext.SValue
			//return true
		}
		if prop.Type == "enum" && pContext.PropertyPtr != nil && pContext.PropertyPtr.NameValues != nil {
			for _, v := range pContext.PropertyPtr.NameValues {
				sValue := strconv.FormatInt(pContext.IValue, 10)
				if v.Value == sValue {
					pContext.Display = v.Name
					break

				}
			}
		}
		return true
	case "bool":
		pContext.Type = "bool"
		if strok {
			bv := strings.ToLower(svalue)
			pContext.SValue = bv
			if bv == "true" || bv == "1" {
				pContext.IValue = 1
				pContext.FValue = 1
				pContext.ValuePtr.Value = true
				pContext.SValue = "1"
				pContext.Display = "true"
			} else {
				pContext.IValue = 0
				pContext.FValue = 0
				pContext.ValuePtr.Value = false
				pContext.SValue = "0"
				pContext.Display = "false"
			}
			/*if pContext.ValuePtr.PID == "UPS_broke" {
				fmt.Println("***********111111*****", pContext.SValue)
			}
			if prop.PID == "power" && prop.SubID == "delay0" {
				fmt.Println("-----------------bool-----11111111-------@@@@@@@@@",
					pContext.ValuePtr.Mindex, prop.PID, prop.SubID, pContext.ValuePtr.Value,
					pContext.SValue)
			}*/
			return true
		}
		if !changeNumber(pValue, pContext) {
			logger.SLog.Error("----------type error 3-bool---------", pValue, reflect.TypeOf(pContext.ValuePtr.Value).Name())
			return false
		} else {
			pContext.SValue = fmt.Sprintf("%d", pContext.IValue)
			if pContext.IValue == 1 {
				pContext.ValuePtr.Value = true
			} else {
				pContext.ValuePtr.Value = false
			}
			/*if pContext.ValuePtr.PID == "UPS_broke" {
				fmt.Println("***********222221*****", pContext.SValue)
			}*/
		}
		pContext.Display = pContext.SValue
		/*
			if prop.PID == "power" && prop.SubID == "delay0" {
				fmt.Println("-----------------bool------------@@@@@@@@@",
					pContext.ValuePtr.Mindex, prop.PID, prop.SubID, pContext.ValuePtr.Value,
					pContext.SValue)
			}*/

		return true

		//return true
	}

	return false

}

func getValue(pContext *ValueContext) string {
	return pContext.SValue
}

func generateSelfEvent(pContext *ValueContext) {
	//当设备不是本机管理时
	if pContext.ThingPtr.Thing.ProxyThingID != "" && pContext.ThingPtr.Thing.ProxyThingID != config.SelfOption.ID {
		return
	}
	exprs, err := cache.GetExpressions(
		pContext.ThingPtr.Thing.UID,
		pContext.ThingPtr.Thing.ModelID, pContext.ValuePtr.PID, pContext.ValuePtr.SubID)
	if err == nil && len(exprs) > 0 {
		var sexpr = ""
		var value = ""
		if pContext.ValuePtr.SubID != "" {
			sexpr = "{{" + pContext.ValuePtr.PID + "." + pContext.ValuePtr.SubID + "}}"
			value = getValue(pContext)

		} else {
			sexpr = "{{" + pContext.ValuePtr.PID + "}}"
			value = getValue(pContext)
		}

		for i := range exprs {
			if len(exprs[i].Source) == 1 {
				//expr :=
				expr := strings.ReplaceAll(exprs[i].Expression, sexpr, value)
				expression, err := govaluate.NewEvaluableExpression(expr)
				if err != nil {
					continue
				}
				result, errexp := expression.Evaluate(nil)
				if errexp != nil {
					continue
				}
				isBegin, ok := result.(bool)
				if ok {
					curAlarm := &eventtype.Alarm{}
					curAlarm.TID = pContext.ThingPtr.Thing.UID
					curAlarm.Mindex = int64(pContext.ValuePtr.Mindex)
					curAlarm.AlarmID = exprs[i].TargetID
					if isBegin {
						curAlarm.Flag = "BEGIN"
					} else {
						curAlarm.Flag = "END"
					}
					if curAlarm.Properties == nil {
						curAlarm.Properties = make([]propertytype.Value, 1)
						curAlarm.Properties[0] = *pContext.ValuePtr
					} else {
						curAlarm.Properties = append(curAlarm.Properties, *pContext.ValuePtr)
					}
					notify.RawAlarm(curAlarm)
					isSave, ok := pContext.Context["isSaveDB"]
					if !ok {
						saveData(pContext)
					} else {
						bsave, _ := isSave.(bool)
						if !bsave {
							saveData(pContext)
						}
					}

				} else {
					continue
				}
			}
		}
	}
}
